package distributed

import (
	"fmt"
	"github.com/go-redis/redis"
	"github.com/samuel/go-zookeeper/zk"
	"sync"
	"time"
)

type Lock struct {
	c chan struct{}
}

func NewLock() Lock {
	var l Lock
	l.c = make(chan struct{}, 1)
	l.c <- struct{}{}
	return l
}

func (l Lock) TryLock() bool {
	lockResult := false
	select {
	case <-l.c:
		lockResult = true
	default:

	}
	return lockResult
}

func (l Lock) UnLock() {
	l.c <- struct{}{}
}

var counter = 0

var myLock sync.RWMutex

func TryLock() {
	//var l = NewLock()
	var wg sync.WaitGroup
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			if !myLock.TryLock() {
				// log error
				println("lock failed")
				return
			}
			counter++
			println("current counter", counter)
			myLock.Unlock()
		}()
	}
	wg.Wait()
}

func Incr() {
	client := redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "", // no password set
		DB:       0,  // use default DB
	})

	var lockKey = "counter_lock"
	var counterKey = "counter"

	isLock, err := client.SetNX(lockKey, 1, time.Second*5).Result()
	if err != nil {
		panic(err)
	}

	if !isLock {
		fmt.Println("lock fail")
		return
	}

	// counter ++
	getResp := client.Get(counterKey)
	cntValue, err := getResp.Int64()
	if err == nil {
		cntValue++
		resp := client.Set(counterKey, cntValue, 0)
		_, err := resp.Result()
		if err != nil {
			// log err
			println("set value error!")
		}
	}
	println("current counter is ", cntValue)

	delResp := client.Del(lockKey)
	unlockSuccess, err := delResp.Result()
	if err == nil && unlockSuccess > 0 {
		println("unlock success!")
	} else {
		println("unlock failed", err)
	}
}

// tryLock 锁
func RedisLock() {
	var wg sync.WaitGroup
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			Incr()
		}()
	}
	wg.Wait()
}

// 阻塞锁
func zkLock() {
	c, _, err := zk.Connect([]string{"127.0.0.1"}, time.Second) //*10)
	if err != nil {
		panic(err)
	}
	l := zk.NewLock(c, "/lock", zk.WorldACL(zk.PermAll))
	err = l.Lock()
	if err != nil {
		panic(err)
	}
	println("lock success, do your business logic")

	time.Sleep(time.Second * 10)

	// do some thing
	err = l.Unlock()
	if err != nil {
		panic(err)
	}
	println("unlock success, finish business logic")
}

//func EtcdLock() {
//	m, err := etcdsync.New("/lock", 10, []string{"http://127.0.0.1:2379"})
//	if m == nil || err != nil {
//		log.Printf("etcdsync.New failed")
//		return
//	}
//	err = m.Lock()
//	if err != nil {
//		log.Printf("etcdsync.Lock failed")
//		return
//	}
//
//	log.Printf("etcdsync.Lock OK")
//	log.Printf("Get the lock. Do something here.")
//
//	err = m.Unlock()
//	if err != nil {
//		log.Printf("etcdsync.Unlock failed")
//	} else {
//		log.Printf("etcdsync.Unlock OK")
//	}
//}
